home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 …ember: Reference Library / Apple Developer Reference Library (December 1999) (Disk 1).iso / pc / technical documentation / macintosh technotes and q&as / technotes / tn / samplecode.sit.hqx / Sample Code / GetShapeLocalBounds.cp < prev    next >
Encoding:
Text File  |  1997-07-22  |  12.7 KB  |  444 lines

  1. /*
  2.     File:        GetShapeLocalBounds.cp
  3.  
  4.     Contains:    This file implements a custom version of GXGetShapeLocalBounds
  5.                 that fixes a problem with handling the bounds of an embedded
  6.                 QuickDraw picture.
  7.  
  8.     Written by:    Daniel Lipton
  9.  
  10.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Writers:
  13.  
  14.         (DIL)    Daniel Lipton
  15.         (IK)    Ingrid Kelly
  16.  
  17.     Change History (most recent first):
  18.  
  19.          <1>      6/1/97    IK        First created.
  20. */
  21.  
  22. #include <GXGraphics.h>
  23.  
  24. #include "GetShapeLocalBounds.h"
  25.  
  26. /* ---------------------------------------------------------------------------
  27.     The transform stack class. This is used for building a stack of
  28.     concatenated transforms for doing the bounding box calculation in pictures.
  29.   --------------------------------------------------------------------------- */
  30.  
  31. class GXBoundingBoxTransformStack {
  32. public:
  33.                     GXBoundingBoxTransformStack();
  34.                     ~GXBoundingBoxTransformStack();
  35.     void            Push(gxTransform aTransform);
  36.     void            Pop();
  37.     void            AccumulateBounds(gxShape aShape);
  38.     void            GetBoundsOfMappedShape(gxShape aShape, gxRectangle *mappedBounds);
  39.     gxRectangle*    GetAccumulatedBounds(gxRectangle *bounds)
  40.                     {    *bounds = accumulatedBounds; return bounds; };
  41. protected:
  42.     gxRectangle        rootClipBounds;                /*    The bounding box of the root clip. */
  43.     gxRectangle        accumulatedBounds;            /*    The bounds we've accumulated so far. */
  44.     gxTransform*    transformList;                /*    The list of transforms on the stack. */
  45.     long            transformCount;                /*    How many transforms on the stack. */
  46.     long            listSize;                    /*    How big (in gxTransforms) is the list allocated. */
  47.  
  48.     gxTransform        GetTop() {
  49.                         if ( transformCount > 0 ) return (transformList[transformCount-1]);
  50.                         else return nil;
  51.                     }
  52. };
  53.  
  54. /* ---------------------------------------------------------------------------
  55.     GXTestTransformIdentity
  56.   --------------------------------------------------------------------------- */
  57.  
  58. static Boolean GXTestTransformIdentity(gxTransform aTransform, gxShape *returnClip)
  59. {
  60.     gxMapping            theMapping;
  61.     gxShape                clip = GXGetTransformClip(aTransform);
  62.     gxShapeType            clipType = GXGetShapeType(clip);
  63.     register Fixed        *mapElement;
  64.  
  65.     if ( returnClip )
  66.         *returnClip = clip;
  67.     else
  68.         GXDisposeShape(clip);
  69.  
  70.     if ( clipType != gxFullType )
  71.         return false;
  72.         
  73.     /*    If the clip was full, then proceed to test the mapping. */
  74.  
  75.     GXGetTransformMapping(aTransform, &theMapping);
  76.     mapElement = &(theMapping.map[0][0]);
  77.                 
  78.     /** Note, this expression depends on evaluation in left-right order. **/
  79.  
  80.     return     ( (*mapElement++ == ff(1)) &&
  81.             (*mapElement++ == ff(0)) &&
  82.             (*mapElement++ == ff(0)) &&
  83.  
  84.             (*mapElement++ == ff(0)) &&
  85.             (*mapElement++ == ff(1)) &&
  86.             (*mapElement++ == ff(0)) &&
  87.  
  88.             (*mapElement++ == ff(0)) &&
  89.             (*mapElement++ == ff(0)) &&
  90.             (*mapElement   == fract1)
  91.             );
  92. }
  93.  
  94. /* ---------------------------------------------------------------------------
  95.     GXBoundingBoxTransformStack
  96.   --------------------------------------------------------------------------- */
  97.  
  98. GXBoundingBoxTransformStack::GXBoundingBoxTransformStack()
  99. {
  100.     accumulatedBounds.left = gxPositiveInfinity;
  101.     accumulatedBounds.top = gxPositiveInfinity;
  102.     accumulatedBounds.right = gxNegativeInfinity;
  103.     accumulatedBounds.bottom = gxNegativeInfinity;
  104.     transformList = nil;
  105.     transformCount = 0;
  106.     listSize = 0;
  107. }
  108.  
  109. /* ---------------------------------------------------------------------------
  110.     ~GXBoundingBoxTransformStack
  111.   --------------------------------------------------------------------------- */
  112.  
  113. GXBoundingBoxTransformStack::~GXBoundingBoxTransformStack()
  114. {
  115.     if ( transformList != nil )
  116.     {
  117.         for ( long idx = 0; idx < transformCount; ++idx )
  118.             GXDisposeTransform(transformList[idx]);
  119.  
  120.         if ( transformList != nil)
  121.             delete transformList;        
  122.     }
  123. }
  124.  
  125. /* ---------------------------------------------------------------------------
  126.     GetBoundsOfMappedShape
  127.   --------------------------------------------------------------------------- */
  128.  
  129. void GXBoundingBoxTransformStack::GetBoundsOfMappedShape(gxShape aShape, gxRectangle *mappedBounds)
  130. {
  131.     gxShapeType        theType = GXGetShapeType(aShape);
  132.     gxShapeFill        theFill = GXGetShapeFill(aShape);
  133.     gxShape            tempShape;
  134.     gxMapping        nestMap;
  135.  
  136.     if ( theFill == gxNoFill )
  137.     {
  138.         mappedBounds->left = gxPositiveInfinity;
  139.         mappedBounds->top = gxPositiveInfinity;
  140.         mappedBounds->right = gxNegativeInfinity;
  141.         mappedBounds->bottom = gxNegativeInfinity;
  142.         return;
  143.     }
  144.         
  145.     /*    Make a copy of the shape so we can take it into "root" space. */
  146.  
  147.     if ( theType != gxBitmapType )
  148.     {
  149.         tempShape = GXCopyToShape(nil, aShape);
  150.  
  151.         /*    If it is framed, we need to apply the style to get accurate bounds. */
  152.  
  153.         if ( (theFill == gxFrameFill) || (theFill == gxClosedFrameFill) )
  154.             if ( GXGetShapePen(tempShape) != 0 )
  155.                 GXPrimitiveShape(tempShape);
  156.     }
  157.     else
  158.     {
  159.         /*    For bitmaps, we can just use a rectangle for our calculations */
  160.  
  161.         gxPoint            location;
  162.         gxBitmap        theBits;
  163.         gxRectangle        bitRect;
  164.  
  165.         GXGetBitmap(aShape, &theBits, &location);
  166.         bitRect.left = location.x;
  167.         bitRect.top = location.y;
  168.         bitRect.right = bitRect.left + ff(theBits.width);
  169.         bitRect.bottom = bitRect.top + ff(theBits.height);
  170.         tempShape = GXNewRectangle(&bitRect);
  171.     }
  172.     
  173.     /*    Now take the shape into "root" space. */
  174.  
  175.     GXSetShapeAttributes(tempShape, GXGetShapeAttributes(tempShape) & ~gxMapTransformShape);
  176.  
  177.     GXGetTransformMapping(this->GetTop(), &nestMap);
  178.     GXMapShape(tempShape, &nestMap);
  179.     
  180.     /*    Now shape is in "root" space, get its bounds. */
  181.  
  182.     gxTransform aTransform = GXNewTransform();
  183.     GXResetTransform(aTransform);
  184.     GXSetShapeTransform(tempShape, aTransform);
  185.     GXDisposeTransform(aTransform);
  186.  
  187.     /*    This one ignores style and transform, but we've taken care of it. */
  188.     GXGetShapeLocalBounds(tempShape, mappedBounds);
  189.     GXDisposeShape(tempShape);
  190. }
  191.  
  192. /* ---------------------------------------------------------------------------
  193.     Push
  194.   --------------------------------------------------------------------------- */
  195.  
  196. void GXBoundingBoxTransformStack::Push(gxTransform aTransform)
  197. {        
  198.     /*    Make room for next transform if there isn't any. */
  199.  
  200.     if ( transformCount >= listSize )
  201.     {
  202.         gxTransform *newList = new gxTransform[listSize+10];
  203.         for ( long idx = 0; idx < listSize; ++idx )
  204.             newList[idx] = transformList[idx];
  205.  
  206.         if ( transformList != nil )
  207.             delete [] transformList;
  208.         transformList = newList;
  209.         listSize += 10;
  210.     }
  211.  
  212.     /*    Now push the next transform on the stack. */
  213.  
  214.     gxShape        clip;
  215.     gxMapping    mapping;
  216.     gxTransform newTransform = nil;
  217.  
  218.     if ( transformCount == 0 )
  219.     {
  220.         /*    Make the root transform. */
  221.  
  222.         newTransform = GXNewTransform();
  223.         GXResetTransform(newTransform);
  224.         GXGetTransformMapping(aTransform, &mapping);
  225.         
  226.         /*    Get the clip and put it into "root" space. */
  227.  
  228.         clip = GXGetTransformClip(aTransform);
  229.         GXSetShapeAttributes(clip, GXGetShapeAttributes(clip) & ~gxMapTransformShape);
  230.         GXMapShape(clip, &mapping);
  231.         GXSetTransformMapping(newTransform, &mapping);
  232.         GXSetTransformClip(newTransform, clip);
  233.  
  234.         /*    Now get the bounds of the new root clip. */
  235.  
  236.         GXGetShapeBounds(clip, 0, &rootClipBounds);
  237.  
  238.         GXDisposeShape(clip);
  239.     }
  240.     else
  241.     {
  242.         /*    Concatenate this transform with the last one. */
  243.  
  244.         Boolean isIdentity = GXTestTransformIdentity(aTransform, &clip);
  245.  
  246.         if ( isIdentity )
  247.         {
  248.             newTransform = GXCopyToTransform(nil, this->GetTop());
  249.         }
  250.         else
  251.         {
  252.             newTransform = GXNewTransform();
  253.             
  254.             /*    Give it the mapping that is the top of the stack mapped by the new mapping. */
  255.  
  256.             gxMapping topMapping;
  257.             GXGetTransformMapping(this->GetTop(), &topMapping);
  258.             GXGetTransformMapping(aTransform, &mapping);
  259.             MapMapping(&mapping, &topMapping);
  260.             GXSetTransformMapping(newTransform, &mapping);
  261.             
  262.             /*    Now take the new clip into "root" space. */
  263.  
  264.             clip = GXGetTransformClip(aTransform);
  265.             GXSetShapeAttributes(clip, GXGetShapeAttributes(clip) & ~gxMapTransformShape);
  266.  
  267.             /*    Map the clip by the concatenated mapping, puts clip in "root" space. */
  268.  
  269.             GXMapShape(clip, &mapping);
  270.  
  271.             /*    Now intersect it with the old "root" clip. */
  272.  
  273.             gxShape rootClip = GXGetTransformClip(this->GetTop());
  274.             GXIntersectShape(rootClip, clip);
  275.             GXSetTransformClip(newTransform, rootClip);
  276.                             
  277.             GXGetShapeBounds(rootClip, 0, &(this->rootClipBounds));        /*    Update the current clip bounds. */
  278.             GXDisposeShape(rootClip);
  279.             GXDisposeShape(clip);
  280.         }
  281.     }
  282.     
  283.     transformList[transformCount++] = newTransform;                        /*    Push it onto the stack. */
  284. }
  285.  
  286. /* ---------------------------------------------------------------------------
  287.     Pop
  288.   --------------------------------------------------------------------------- */
  289.  
  290. void GXBoundingBoxTransformStack::Pop()
  291. {
  292.     if ( transformCount > 0 )
  293.     {
  294.         transformCount -= 1;    
  295.         GXDisposeTransform(transformList[transformCount]);
  296.     }
  297.  
  298.     gxTransform topTransform = this->GetTop();
  299.     if ( topTransform != nil )
  300.     {
  301.         gxShape topClip = GXGetTransformClip(topTransform);
  302.         GXGetShapeBounds(topClip, 0, &rootClipBounds);                    /*    Reset the root clip bounds box. */
  303.         GXDisposeShape(topClip);
  304.     }
  305.     else
  306.     {
  307.         rootClipBounds.left = gxNegativeInfinity;
  308.         rootClipBounds.top = gxNegativeInfinity;
  309.         rootClipBounds.right = gxPositiveInfinity;
  310.         rootClipBounds.bottom = gxPositiveInfinity;
  311.     }
  312. }
  313.  
  314. /* ---------------------------------------------------------------------------
  315.     AccumulateBounds
  316.   --------------------------------------------------------------------------- */
  317.  
  318. void GXBoundingBoxTransformStack::AccumulateBounds(gxShape aShape)
  319. {
  320.     gxRectangle            thisBounds;
  321.     
  322.     this->GetBoundsOfMappedShape(aShape, &thisBounds);                
  323.  
  324.     /*    If the thing has a wide open rectange and it isn't a full-shape, then
  325.         this is one of those wierd GX bugs, the shape probably has no bounds.
  326.     */
  327.     if ( (thisBounds.left == gxNegativeInfinity) && (thisBounds.top == gxNegativeInfinity) &&
  328.             (thisBounds.bottom == gxPositiveInfinity) && (thisBounds.right == gxPositiveInfinity) )
  329.     {
  330.         thisBounds.left = gxPositiveInfinity;
  331.         thisBounds.top = gxPositiveInfinity;
  332.         thisBounds.right = gxNegativeInfinity;
  333.         thisBounds.bottom = gxNegativeInfinity;
  334.     }
  335.     
  336.     /*    Now intersect it with the bounds of the "root" clip. */
  337.  
  338.     if ( thisBounds.left < rootClipBounds.left )
  339.         thisBounds.left = rootClipBounds.left;
  340.     if ( thisBounds.top < rootClipBounds.top )
  341.         thisBounds.top = rootClipBounds.top;
  342.     if ( thisBounds.bottom > rootClipBounds.bottom )
  343.         thisBounds.bottom = rootClipBounds.bottom;
  344.     if ( thisBounds.right > rootClipBounds.right )
  345.         thisBounds.right = rootClipBounds.right;
  346.     
  347.     /*    Now accumulate the bounds into the total. */
  348.  
  349.     if ( thisBounds.left < accumulatedBounds.left )
  350.         accumulatedBounds.left = thisBounds.left;
  351.     if ( thisBounds.right > accumulatedBounds.right )
  352.         accumulatedBounds.right = thisBounds.right;
  353.     if ( thisBounds.top < accumulatedBounds.top )
  354.         accumulatedBounds.top = thisBounds.top;
  355.     if ( thisBounds.bottom > accumulatedBounds.bottom )
  356.         accumulatedBounds.bottom = thisBounds.bottom;
  357. }
  358.  
  359. /* ---------------------------------------------------------------------------
  360.     ComputePictureBounds
  361.   --------------------------------------------------------------------------- */
  362.  
  363. static void ComputePictureBounds(gxShape source, GXBoundingBoxTransformStack *tStack)
  364. {
  365.     tStack->Push(GXGetShapeTransform(source));
  366.  
  367.     gxShapeType theType = GXGetShapeType(source);
  368.  
  369.     if ( theType == gxPictureType ) 
  370.     {
  371.         gxStyle            overridingStyle, oldStyle;
  372.         gxTransform        oldTransform, overridingTransform;        /*    oldTransform declared above. */
  373.         gxShape            child;
  374.                 
  375.         long nShapes = GXGetPictureParts(source, 1, gxSelectToEnd, nil, nil, nil, nil);
  376.  
  377.         for ( long idx = 1; idx <= nShapes; ++idx )
  378.         {
  379.             /*    Get the next child from the picture. */
  380.             
  381.             GXGetPictureParts(source, idx, 1, &child, &overridingStyle, nil, &overridingTransform);
  382.             
  383.             /*    Now apply the overriding attributes (we don't care about Ink, though
  384.                 because it does not affect bounds).
  385.             */
  386.             if ( overridingStyle )
  387.             {
  388.                 oldStyle = GXCloneStyle(GXGetShapeStyle(child));
  389.                 GXSetShapeStyle(child, overridingStyle);
  390.             }
  391.             if ( overridingTransform )
  392.             {
  393.                 oldTransform = GXCloneTransform(GXGetShapeTransform(child));
  394.                 GXSetShapeTransform(child, overridingTransform);
  395.             }
  396.             
  397.             ComputePictureBounds(child, tStack);
  398.                             
  399.             /*    Now restore the shapes orginal attributes if we modified them with
  400.                 overrides.
  401.             */
  402.             if ( overridingStyle )
  403.             {
  404.                 GXSetShapeStyle(child, oldStyle);
  405.                 GXDisposeStyle(oldStyle);
  406.             }
  407.             if ( overridingTransform )
  408.             {
  409.                 GXSetShapeTransform(child, oldTransform);
  410.                 GXDisposeTransform(oldTransform);
  411.             }
  412.         }
  413.     }
  414.     else if ( theType != gxEmptyType )
  415.     {
  416.         tStack->AccumulateBounds(source);
  417.     }
  418.  
  419.     tStack->Pop();
  420. }
  421.  
  422. #ifdef __cplusplus
  423. extern "C" {
  424. #endif
  425.  
  426. /* ---------------------------------------------------------------------------
  427.     GetShapeLocalBounds
  428.   --------------------------------------------------------------------------- */
  429.  
  430. gxRectangle* GetShapeLocalBounds(gxShape source, gxRectangle *bounds)
  431. {
  432.     GXBoundingBoxTransformStack    tStack;
  433.     gxRectangle                    accumulatedBounds;
  434.  
  435.     ComputePictureBounds(source, &tStack);
  436.     *bounds = *(tStack.GetAccumulatedBounds(&accumulatedBounds));
  437.  
  438.     return bounds;
  439. }
  440.  
  441. #ifdef __cplusplus
  442. }
  443. #endif
  444.